﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Soapi.Domain;
using Soapi.Parameters;
using ListBox = System.Windows.Controls.ListBox;
using Timer = System.Threading.Timer;
using Soapi.Collections;
namespace Soapi.Notify2
{
    public partial class MainForm : Form
    {
        private QuestionsCollection _questions;
        private object _syncLock = new object();
        private System.Threading.Timer _timer;
        public MainForm()
        {
            InitializeComponent();
            _questions = new QuestionsCollection();
            QuestionsListBoxItems.ItemsSource = _questions;

        }

        private void Process(object state)
        {
            Program.Settings.Sites.Where(site => site.Enabled).ToList().ForEach(ss =>
                {
                    lock (_syncLock)
                    {
                        if (_timer != null)
                        {
                            _timer.Dispose();
                            _timer = null;
                        }
                    }

                    // get existing questions for update

                    ThreadPool.QueueUserWorkItem(s =>
                        {
                            var site = (SiteListItem)s;

                            var lastProcessTime = site.LastProcessTime;

                            var gate = new AutoResetEvent(false);
                            BeginInvoke(new MethodInvoker(() =>
                            {
                                lock (_syncLock)
                                {
                                    var toUpdate =_questions.Where(q => q.Site.ApiEndpoint == site.ApiEndpoint).ToList();
                                    ThreadPool.QueueUserWorkItem(UpdateQuestions, toUpdate);
                                    gate.Set();
                                }
                            }));

                            gate.WaitOne();

                            if (lastProcessTime == DateTime.MinValue)
                            {
                                lastProcessTime = DateTime.Now.AddSeconds(-(Program.Settings.PollingInterval));
                            }
                            site.LastProcessTime = DateTime.Now; // this might be an issue. might need to initialize the last processed time on startup
                            var questions = site.Site.Questions.Sort(QuestionSort.Creation).PageSize(100).FromDate(lastProcessTime.ToUniversalTime()).ToDate(new DateTime(2020, 10, 10)).ToList();

                            // cherry pick included tags, if specified.
                            var includedTags = (site.IncludedTags ?? "").ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                            if (includedTags.Length > 0)
                            {
                                questions = questions.Where(q => q.Tags.Any(t => includedTags.Contains(t))).ToList();
                            }

                            // remove excluded tags, if specified
                            var excludedTags = (site.ExcludedTags ?? "").ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                            if (excludedTags.Length > 0)
                            {
                                questions = questions.Where(q => q.Tags.All(t =>
                                    {
                                        if (excludedTags.Contains(t))
                                        {
                                            return false;
                                        }
                                        return true;
                                    })).ToList();
                            }

                            ////TODO: depends on growl
                            //var pinnedTags = (site.PinnedTags ?? "").ToLower().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                            //List<Question> pinnedQuestions;
                            //if (pinnedTags.Length > 0)
                            //{
                            //    pinnedQuestions = questions.Where(q => q.Tags.Any(t => pinnedTags.Contains(t))).ToList();
                            //}


                            BeginInvoke(new MethodInvoker(() =>
                            {
                                lock (_syncLock)
                                {
                                    var selected=new List<int>();
                                    foreach (Question item in QuestionsListBoxItems.SelectedItems)
                                    {
                                        selected.Add(item.QuestionId);
                                    }

                         
                                    questions.ForEach(q =>
                                    {
                                        var existing = _questions.FirstOrDefault(qq => qq.QuestionId == q.QuestionId);
                                        if (existing != null)
                                        {
                                            var index = _questions.IndexOf(existing);
                                            //_questions.RemoveAt(index);
                                            _questions[index] = q;

                                        }
                                        else
                                        {
                                            _questions.Add(q);
                                        }


                                    });


                                    _questions.Sort();

                                    foreach (int id in selected)
                                    {
                                        int id1 = id;
                                        var selectedQuestion = _questions.FirstOrDefault(q => q.QuestionId == id1);
                                        if (selectedQuestion != null)
                                        {
                                            QuestionsListBoxItems.SelectedItems.Add(selectedQuestion);
                                        }
                                    }
                                }
                            }));

                        }, ss);

                    lock (_syncLock)
                    {
                        _timer = new Timer(Process, null, Program.Settings.PollingInterval * 1000, Timeout.Infinite);
                    }

                });
        }

        private void UpdateQuestions(object state)
        {

            BeginInvoke(new MethodInvoker(() =>
            {
                lock (_syncLock)
                {
                    var selected = new List<int>();
                    foreach (Question item in QuestionsListBoxItems.SelectedItems)
                    {
                        selected.Add(item.QuestionId);
                    }

                    var qToUpdate = (List<Question>)state;
                    if (qToUpdate.Count == 0)
                    {
                        return;
                    }
                    var site = Program.Context.Find(qToUpdate[0].Site.ApiEndpoint);


                    IEnumerable<IEnumerable<int>> batches = qToUpdate.Select(q => q.QuestionId).InBatches(100);
                    foreach (IEnumerable<int> batche in batches)
                    {
                        DateTime lastUpdate = Program.Settings.Sites.Find(site.ApiEndpoint).LastProcessTime;
                        var questions = site.Questions.ById(batche).Sort(PostSort.Activity).Min(lastUpdate).Max(new DateTime(2050, 10, 10)).PageSize(100).ToList();

                        questions.ForEach(q =>
                            {
                                var existing = _questions.FirstOrDefault(qq => qq.QuestionId == q.QuestionId);
                                if (existing != null)
                                {
                                    var index = _questions.IndexOf(existing);
                                    //_questions.RemoveAt(index);
                                    _questions[index] = q;

                                }

                            });

                    }

                    foreach (int id in selected)
                    {
                        int id1 = id;
                        var selectedQuestion = _questions.FirstOrDefault(q => q.QuestionId == id1);
                        if (selectedQuestion != null)
                        {
                            QuestionsListBoxItems.SelectedItems.Add(selectedQuestion);
                        }
                    }
                }
            }));
        }

        private void SettingsToolStripButton_Click(object sender, EventArgs e)
        {
            var sf = new SettingsForm();
            if (sf.ShowDialog(this) == DialogResult.OK)
            {
                ApplySettings();

            }
        }

        private void WatchQuestionToolStripButton_Click(object sender, EventArgs e)
        {
            var wqf = new WatchQuestionForm();

            if (wqf.ShowDialog(this) == DialogResult.OK)
            {
                if (wqf.SiteBindingSource.Current != null)
                {
                    var site = (Site)wqf.SiteBindingSource.Current;
                }
            }

        }
        private ListBox QuestionsListBoxItems
        {
            get
            {
                return (ListBox)((QuestionsListView)elementHost1.Child).FindName("QuestionListBox");
            }
        }
        private void MainForm_Shown(object sender, EventArgs e)
        {
            Program.Settings.Sites.UpdateSiteIcons();

            if (Program.Settings.Sites.All(s => s.Enabled == false))
            {
                SettingsToolStripButton_Click(this, EventArgs.Empty);
            }
            else
            {
                ApplySettings();
            }


        }
        private void ApplySettings()
        {
            lock (_syncLock)
            {
                _questions.Clear();
                // need to close non-pinned notifications
                if (_timer != null)
                {
                    _timer.Dispose();
                    _timer = null;
                }
                if (Program.Settings.PollingInterval < 10)
                {
                    Program.Settings.PollingInterval = 10;
                    Program.Settings.Save();
                }

                Program.Settings.Sites.ForEach(

                    s => s.LastProcessTime = DateTime.Now.AddSeconds(-(Program.Settings.PollingInterval)));
                _timer = new Timer(Process, null, 0, Timeout.Infinite);
            }
        }

        private void ClearToolStripButton_Click(object sender, EventArgs e)
        {
            lock (_syncLock)
            {
                _questions.Clear();
            }
        }

        private void HelpAboutToolStripButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show("TODO");
        }
    }

    public class QuestionsCollection : ObservableCollection<Question>
    {



        public void Sort()
        {

            int i, j;

            Question index;

            for (i = 1; i < Count; i++)
            {

                index = this[i];     //If you can't read it, it should be index = this[x], where x is i :-)

                j = i;

                while ((j > 0) && (index.CreationDate.CompareTo(this[j - 1].CreationDate) == 1))
                {
                    this[j] = this[j - 1];
                    j = j - 1;
                }
                this[j] = index;
            }

        }

    }
}
